﻿/*

The MIT License

Copyright (c) 2010 Cartesian Analytics, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

using System;
using System.Diagnostics;

namespace Pipra.Math.Geometry
{
    /// <summary>
    /// A vector in 3D space.
    /// </summary>
    public struct Vector3 :
        IVector3<double>,
        IEquatable<Vector3>,
        IComparable<Vector3>
    {
        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">A vector.</param>
        /// <param name="b">A vector.</param>
        /// <returns>True if both vectors have the same component values.</returns>
        public static bool operator ==(Vector3 a, Vector3 b)
        {
            return a.Equals(b);
        }
        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">A vector.</param>
        /// <param name="b">A vector.</param>
        /// <returns>True if both vectors do not have the same component values.</returns>
        public static bool operator !=(Vector3 a, Vector3 b)
        {
            return !a.Equals(b);
        }

        /// <summary>
        /// A vector with all components set to zero.
        /// </summary>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public static readonly Vector3 ZeroVector = new Vector3(0, 0, 0);
        /// <summary>
        /// A vector with a magnitude of one and oriented in the direction of the positive X axis.
        /// </summary>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public static readonly Vector3 XUnitVector = new Vector3(1, 0, 0);
        /// <summary>
        /// A vector with a magnitude of one and oriented in the direction of the positive Y axis.
        /// </summary>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public static readonly Vector3 YUnitVector = new Vector3(0, 1, 0);
        /// <summary>
        /// A vector with a magnitude of one and oriented in the direction of the positive Z axis.
        /// </summary>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public static readonly Vector3 ZUnitVector = new Vector3(0, 0, 1);

        /// <summary>
        /// The X componenet of this point.
        /// </summary>
        public readonly double X;
        /// <summary>
        /// The Y componenet of this point.
        /// </summary>
        public readonly double Y;
        /// <summary>
        /// The Z componenet of this point.
        /// </summary>
        public readonly double Z;
        /// <summary>
        /// Creates a 2D vector.
        /// </summary>
        /// <param name="x">The x coordinate.</param>
        /// <param name="y">The y coordinate.</param>
        /// <param name="z">The z coordinate.</param>
        public Vector3(double x, double y, double z) {
			X = x;
			Y = y;
			Z = z;
		}
        /// <summary>
        /// Creates a 2D vector.
        /// </summary>
        /// <param name="v">The coordinate tuple to copy values from.</param>
		public Vector3(ICoordinateTriple<double> v) {
			X = v.X;
			Y = v.Y;
			Z = v.Z;
		}

        /// <summary>
        /// The X componenet of this point.
        /// </summary>
        double ICoordinateTriple<double>.X
        {
            get { return X; }
        }
        /// <summary>
        /// The Y componenet of this point.
        /// </summary>
        double ICoordinateTriple<double>.Y
        {
            get { return Y; }
        }
        /// <summary>
        /// The Z componenet of this point.
        /// </summary>
        double ICoordinateTriple<double>.Z
        {
            get { return Z; }
        }
        /// <summary>
        /// Calculates the dot product between this vector and another vector.
        /// </summary>
        /// <param name="rhs">Another vector to use for the calculation of the dot product.</param>
        /// <returns>The dot product.</returns>
        public double Dot(Vector3 rhs)
        {
            return (X * rhs.X) + (Y * rhs.Y) + (Z * rhs.Z);
        }
        /// <summary>
        /// Calculates the dot product between this vector and another vector.
        /// </summary>
        /// <param name="rhs">Another vector to use for the calculation of the dot product.</param>
        /// <returns>The dot product.</returns>
        public double Dot(ICoordinateTriple<double> rhs)
        {
            return (X * rhs.X) + (Y * rhs.Y) + (Z * rhs.Z);
        }
        /// <summary>
        /// Calculates the cross product of this vector and another.
        /// </summary>
        /// <param name="rhs">Another vector.</param>
        /// <returns>A vector representing the cross product.</returns>
        public Vector3 Cross(Vector3 rhs)
        {
            return new Vector3(
                (Y * rhs.Z) - (Z * rhs.Y),
                (Z * rhs.X) - (X * rhs.Z),
                (X * rhs.Y) - (Y * rhs.X)
            );
        }
        /// <summary>
        /// Calculates the cross product of this vector and another.
        /// </summary>
        /// <param name="rhs">Another vector.</param>
        /// <returns>A vector representing the cross product.</returns>
        public Vector3 Cross(ICoordinateTriple<double> rhs)
        {
            return new Vector3(
                (Y * rhs.Z) - (Z * rhs.Y),
                (Z * rhs.X) - (X * rhs.Z),
                (X * rhs.Y) - (Y * rhs.X)
            );
        }
        /// <summary>
        /// Calculates the cross product of this vector and another.
        /// </summary>
        /// <param name="rhs">Another vector.</param>
        /// <returns>A vector representing the cross product.</returns>
        IVector3<double> IVector3<double>.Cross(ICoordinateTriple<double> rhs)
        {
            return Cross(rhs);
        }
        /// <summary>
        /// Calculates a vector resulting from adding the given vector to this vector.
        /// </summary>
        /// <param name="rhs">The vector to add.</param>
        /// <returns>A result of adding this vector with the given vector.</returns>
        public Vector3 Add(Vector3 rhs)
        {
            return new Vector3(X + rhs.X, Y + rhs.Y, Z + rhs.Z);
        }
        /// <summary>
        /// Calculates a vector resulting from adding the given vector to this vector.
        /// </summary>
        /// <param name="rhs">The vector to add.</param>
        /// <returns>A result of adding this vector with the given vector.</returns>
        public Point3 Add(Point3 rhs)
        {
            return new Point3(X + rhs.X, Y + rhs.Y, Z + rhs.Z);
        }
        /// <summary>
        /// Calculates a vector resulting from adding the given vector to this vector.
        /// </summary>
        /// <param name="rhs">The vector to add.</param>
        /// <returns>A result of adding this vector with the given vector.</returns>
        public Vector3 Add(ICoordinateTriple<double> rhs)
        {
            return new Vector3(X + rhs.X, Y + rhs.Y, Z + rhs.Z);
        }
        /// <summary>
        /// Calculates a vector resulting from adding the given vector to this vector.
        /// </summary>
        /// <param name="rhs">The vector to add.</param>
        /// <returns>A result of adding this vector with the given vector.</returns>
        IVector3<double> IVector3<double>.Add(ICoordinateTriple<double> rhs)
        {
            return Add(rhs);
        }
        /// <summary>
        /// Calculates the difference between this vector and another vector.
        /// </summary>
        /// <param name="rhs">The vector to subtract.</param>
        /// <returns>A result of subtracting the given vector from this vector.</returns>
        public Vector3 Difference(Vector3 rhs)
        {
            return new Vector3(X - rhs.X, Y - rhs.Y, Z - rhs.Z);
        }
        /// <summary>
        /// Calculates the difference between this vector and another vector.
        /// </summary>
        /// <param name="rhs">The vector to subtract.</param>
        /// <returns>A result of subtracting the given vector from this vector.</returns>
        public Vector3 Difference(ICoordinateTriple<double> rhs)
        {
            return new Vector3(X - rhs.X,Y - rhs.Y, Z - rhs.Z);
        }
        /// <summary>
        /// Calculates the difference between this vector and another vector.
        /// </summary>
        /// <param name="rhs">The vector to subtract.</param>
        /// <returns>A result of subtracting the given vector from this vector.</returns>
        IVector3<double> IVector3<double>.Difference(ICoordinateTriple<double> rhs)
        {
            return new Vector3(X - rhs.X, Y - rhs.Y, Z - rhs.Z);
        }
        /// <summary>
        /// Calculates a vector oriented in the opposite direction.
        /// </summary>
        /// <returns>A vector with the same component values but different signs.</returns>
        public Vector3 GetNegative()
        {
            return new Vector3(-X, -Y, -Z);
        }
        /// <summary>
        /// Calculates a vector oriented in the opposite direction.
        /// </summary>
        /// <returns>A vector with the same component values but different signs.</returns>
        IVector3<double> IVector3<double>.GetNegative()
        {
            return GetNegative();
        }
        /// <summary>
        /// Calculates a vector with the same direction but a magnitude of one.
        /// </summary>
        /// <returns>A unit length vector.</returns>
        public Vector3 GetNormalized()
        {
            double m = GetMagnitude();
            return (
                0 == m
                ? ZeroVector
                : new Vector3(X / m, Y / m, Z / m)
            );
        }
        /// <summary>
        /// Calculates a vector with the same direction but a magnitude of one.
        /// </summary>
        /// <returns>A unit length vector.</returns>
        IVector3<double> IVector3<double>.GetNormalized()
        {
            return GetNormalized();
        }
        /// <summary>
        /// Calculates the magnitude of this vector.
        /// </summary>
        /// <returns>The magnitude.</returns>
        public double GetMagnitude()
        {
            return System.Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
        }
        /// <summary>
        /// Calculates the magnitude of this vector.
        /// </summary>
        /// <returns>The magnitude.</returns>
        public double GetMagnitudeSquared()
        {
            return (X * X) + (Y * Y) + (Z * Z);
        }
        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that is the hash code for this instance.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            return X.GetHashCode();
        }
        /// <summary>
        /// Indicates whether this instance and a specified object are equal.
        /// </summary>
        /// <returns>
        /// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
        /// </returns>
        /// <param name="obj">Another object to compare to.</param><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            return null != obj
                && (
                    (obj is Vector3)
                    ? Equals((Vector3)obj)
                    : (obj is ICoordinateTriple<double> && Equals(obj as ICoordinateTriple<double>))
                )
            ;
        }
        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return String.Concat(X, ',', Y, ',', Z);
        }
        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(Vector3 other)
        {
            return X == other.X && Y == other.Y && Z == other.Z;
        }
        /// <summary>
        /// Indicates whether the current object is equal to another.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(ICoordinateTriple<double> other)
        {
            return !ReferenceEquals(null,other) && X == other.X && Y == other.Y && Z == other.Z;
        }

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: 
        ///                     Value 
        ///                     Meaning 
        ///                     Less than zero 
        ///                     This object is less than the <paramref name="other"/> parameter.
        ///                     Zero 
        ///                     This object is equal to <paramref name="other"/>. 
        ///                     Greater than zero 
        ///                     This object is greater than <paramref name="other"/>. 
        /// </returns>
        /// <param name="other">An object to compare with this object.
        ///                 </param>
        public int CompareTo(Vector3 other)
        {
            int c = X.CompareTo(other.X);
            if (0 == c)
            {
                c = Y.CompareTo(other.Y);
                return 0 == c ? Z.CompareTo(other.Z) : c;
            }
            return c;
        }

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: 
        ///                     Value 
        ///                     Meaning 
        ///                     Less than zero 
        ///                     This object is less than the <paramref name="other"/> parameter.
        ///                     Zero 
        ///                     This object is equal to <paramref name="other"/>. 
        ///                     Greater than zero 
        ///                     This object is greater than <paramref name="other"/>. 
        /// </returns>
        /// <param name="other">An object to compare with this object.
        ///                 </param>
        public int CompareTo(ICoordinateTriple<double> other)
        {
            if (null == other)
            {
                return 1;
            }
            int c = X.CompareTo(other.X);
            if (0 == c)
            {
                c = Y.CompareTo(other.Y);
                return 0 == c ? Z.CompareTo(other.Z) : c;
            }
            return c;
        }
        /// <summary>
        /// Creates a new vector which is scaled from this vector.
        /// </summary>
        /// <param name="factor">The scaling factor.</param>
        /// <returns>A scaled vector.</returns>
        public Vector3 GetScaled(double factor)
        {
            return new Vector3(X * factor, Y * factor, Z * factor);
        }
        /// <summary>
        /// Creates a new vector which is scaled from this vector.
        /// </summary>
        /// <param name="factor">The scaling factor.</param>
        /// <returns>A scaled vector.</returns>
        IVector3<double> IVector3<double>.GetScaled(double factor)
        {
            return GetScaled(factor);
        }

        double IHasDistance<double, IPoint3<double>>.Distance(IPoint3<double> p)
        {
            throw new NotImplementedException();
            // return new Segment3(Point3.ZeroPoint,this).Distance(p);
        }

        double IHasDistance<double, IPoint3<double>>.DistanceSquared(IPoint3<double> p)
        {
            throw new NotImplementedException();
            // return new Segment3(Point3.ZeroPoint,this).DistanceSquared(p);
        }

        bool IIntersectable<IPoint3<double>>.Intersects(IPoint3<double> p)
        {
            throw new NotImplementedException();
            // return new Segment3(Point3.ZeroPoint,this).Intersects(p);
        }


        
    }
}
